﻿using System;
using INSE6260.OnlineBanking.Model.Payee;
using INSE6260.OnlineBanking.Service.Interfaces;
using INSE6260.OnlineBanking.Infrastructure;
using INSE6260.OnlineBanking.Model.Transaction;
using INSE6260.OnlineBanking.Model.Accounts;
using INSE6260.OnlineBanking.Service.Messaging.TransactionService;


namespace INSE6260.OnlineBanking.Service.Implementations
{
    public class TransactionManager : ITransactionManager
    {
        private readonly ITransactionRepository _transactionRepository;
        private readonly IUnitOfWork _uow;
        private readonly IAccountRepository _accountRepository;
        private readonly IPayeeRepository _payeeRepository;
        public TransactionManager(ITransactionRepository transactionRepository,IAccountRepository accountRepository,IPayeeRepository payeeRepository, IUnitOfWork uow)
        {
            _transactionRepository = transactionRepository;
            _accountRepository = accountRepository;
            _payeeRepository = payeeRepository;
            _uow = uow;
        }

        public TransactionResponse Withdraw(WithdrawRequest withdrawRequest)
        {
            if (withdrawRequest.SourceAccountId == null)
                throw new Exception("Account ID is invalid");

            if (withdrawRequest.DestinationAccountId == null)
                throw new Exception("Money card account ID is invalid");

            var account = _accountRepository.GetAccountById(withdrawRequest.SourceAccountId ?? 0);
            var moneyCardAccount = _accountRepository.GetAccountById(withdrawRequest.DestinationAccountId ?? 0);

            var withdraw = new Withdraw
                               {
                                   FromAccountId = account.Id,
                                   ToAccountId = moneyCardAccount.Id,
                                   
                                   Amount = withdrawRequest.Amount,
                                   SourceAccount = account,
                                   DestinationAccount = moneyCardAccount,
                                   TransactionDate = DateTime.Now
                               };

            withdraw.PerformTransaction();
            
            withdraw.WithdrawalAmount = withdraw.Amount;
            withdraw.Description = moneyCardAccount.AccountTypeDiscription + ", " + moneyCardAccount.AccountNo;
            withdraw.Balance = account.Balance;

            var deposit = new Withdraw
                              {
                                  FromAccountId = moneyCardAccount.Id,
                                  ToAccountId = account.Id,
                                  Amount = withdrawRequest.Amount,
                                  SourceAccount = moneyCardAccount,
                                  DestinationAccount = account,
                                  TransactionDate = DateTime.Now
                              };

            deposit.DepositAmount = deposit.Amount;
            deposit.Balance = moneyCardAccount.Balance;
            deposit.Description = "Deposit from " + account.AccountTypeDiscription + "(" + account.AccountNo + ")";


            _transactionRepository.Add(withdraw);
            _transactionRepository.Add(deposit);
            _uow.Commit();

            var transactionResponse = new TransactionResponse
                                          {
                                              Amount = withdrawRequest.Amount,
                                              DestinationAccountNumber =
                                                  moneyCardAccount.AccountNo + "-" +
                                                  moneyCardAccount.AccountTypeDiscription,
                                              SourceAccountNumber =
                                                  account.AccountNo + "-" + account.AccountTypeDiscription,
                                              TransactionType = "Withdraw",
                                              RefNo = withdraw.Id
                                          };

            return transactionResponse;

        }

        public TransactionResponse MakePayment(MakePaymentRequest makePaymentRequest)
        {
            if (makePaymentRequest.SourceAccountId == null)
                throw new Exception("Account ID is invalid");

            if (makePaymentRequest.PayeeId == null)
                throw new Exception("Payee is invalid");

            var account = _accountRepository.GetAccountById(makePaymentRequest.SourceAccountId ?? 0);
            var payee = _payeeRepository.FindByID(makePaymentRequest.PayeeId ?? 0);
            if(payee == null) throw new Exception("Payee is null.");
            var payeeAccount = _accountRepository.GetAccountById(payee.PayeeName.AccountId );

            var payment = new MakePayment
                              {
                                  FromAccountId = account.Id,
                                  ToAccountId = payeeAccount.Id,
                                  Amount = makePaymentRequest.Amount,
                                  SourceAccount = account,
                                  DestinationAccount = payeeAccount,
                                  TransactionDate = DateTime.Now
                              };
            payment.PerformTransaction();

            payment.Description = payee.PayeeName.Name + ", " + payee.AccountNo;
            payment.WithdrawalAmount = payment.Amount;
            payment.Balance = account.Balance;

            var deposit = new MakePayment
                              {
                                  FromAccountId = payeeAccount.Id,
                                  ToAccountId = account.Id,
                                  Amount = makePaymentRequest.Amount,
                                  SourceAccount = payeeAccount,
                                  DestinationAccount = account,
                                  TransactionDate = DateTime.Now
                              };
            deposit.DepositAmount = deposit.Amount;
            deposit.Balance = payeeAccount.Balance;
            deposit.Description =  "Payment: " + payee.AccountNo;

            _transactionRepository.Add(payment);
            _transactionRepository.Add(deposit);
            _uow.Commit();

            var transactionResponse = new TransactionResponse
                                          {
                                              Amount = makePaymentRequest.Amount,
                                              DestinationAccountNumber =
                                                  payeeAccount.AccountNo + "-" + payeeAccount.AccountTypeDiscription +
                                                  "-" + payee.NickName,
                                              SourceAccountNumber =
                                                  account.AccountNo + "-" + account.AccountTypeDiscription,
                                              TransactionType = "Make Payment",
                                              RefNo = payment.Id
                                          };

            return transactionResponse;
        }


        public TransactionResponse Transfer(TransferFundRequest transferFundRequest)
        {
            if (transferFundRequest.SourceAccountId == null)
                throw new Exception("Account ID is invalid");

            if (transferFundRequest.DestinationAccountId == null && string.IsNullOrEmpty(transferFundRequest.DestinationAccountNumber))
                throw new Exception("Destination account is invalid");

            if ( transferFundRequest.SourceAccountId == transferFundRequest.DestinationAccountId)
            {
                throw  new  Exception("The source and destination accounts are the same.");
            }

            var account = _accountRepository.GetAccountById(transferFundRequest.SourceAccountId ?? 0);

            var destinationAccount = transferFundRequest.DestinationAccountId != null ? _accountRepository.GetAccountById(transferFundRequest.DestinationAccountId ?? 0) : _accountRepository.GetAccounByAccountNumber(transferFundRequest.DestinationAccountNumber);


            if ( destinationAccount == null)
                throw  new  Exception("Invalid destination account");
            var transferFrom = new Trasnfer
                                   {
                                       FromAccountId = account.Id,
                                       ToAccountId = destinationAccount.Id,
                                       Amount = transferFundRequest.Amount,
                                       SourceAccount = account,
                                       DestinationAccount = destinationAccount,
                                       TransactionDate = DateTime.Now
                                   };

            
            transferFrom.PerformTransaction();
            transferFrom.Description = "Transfer to Account No:" + destinationAccount.AccountNo;
            transferFrom.WithdrawalAmount = transferFundRequest.Amount;
            transferFrom.Balance = account.Balance;

            var transferTo = new Trasnfer
                                 {
                                     FromAccountId = destinationAccount.Id,
                                     ToAccountId = account.Id,
                                     Amount = transferFundRequest.Amount,
                                     SourceAccount = destinationAccount,
                                     DestinationAccount = account,
                                     TransactionDate = DateTime.Now,
                                     Description = "Transfer from Account: " + account.AccountNo,
                                     DepositAmount = transferFundRequest.Amount,
                                     Balance = destinationAccount.Balance
                                 };



            _transactionRepository.Add(transferFrom);
            _transactionRepository.Add(transferTo);
            _uow.Commit();

            var transactionResponse = new TransactionResponse
                                          {
                                              Amount = transferFundRequest.Amount,
                                              DestinationAccountNumber =
                                                  destinationAccount.AccountNo + "-" +
                                                  destinationAccount.AccountTypeDiscription,
                                              SourceAccountNumber =
                                                  account.AccountNo + "-" + account.AccountTypeDiscription,
                                              TransactionType = "Transfer",
                                              RefNo = transferFrom.Id
                                          };

            return transactionResponse;
        }


        public TransactionResponse Deposit(DepositRequest depositRequest)
        {
            if (string.IsNullOrEmpty(depositRequest.SourceAccountNumber))
                throw new Exception("Invalid Destination Account Number.");
            if (string.IsNullOrEmpty(depositRequest.DestinationAccountNumber))
                throw new Exception("Invalid Destination Account Number.");

            var sourceAccout = _accountRepository.GetAccounByAccountNumber(depositRequest.SourceAccountNumber);
            var destinationAccount = _accountRepository.GetAccounByAccountNumber(depositRequest.DestinationAccountNumber);

            if (sourceAccout == null)
                throw new Exception("Invalid source account number.");


            if ( destinationAccount == null)
                throw new Exception("Invalid destination account number.");

            var depositFrom = new Deposit
                              {
                                  FromAccountId = sourceAccout.Id,
                                  ToAccountId = destinationAccount.Id,
                                  Amount = depositRequest.Amount,
                                  SourceAccount = sourceAccout,
                                  DestinationAccount = destinationAccount,
                                  TransactionDate = DateTime.Now
                              };
            var depositTo = new Deposit
                                {
                                    FromAccountId = destinationAccount.Id,
                                    ToAccountId = sourceAccout.Id,
                                    Amount = depositRequest.Amount,
                                    SourceAccount = destinationAccount,
                                    DestinationAccount = sourceAccout,
                                    TransactionDate = DateTime.Now
                                };

            depositFrom.PerformTransaction();
            depositFrom.Description = "Deposit to Account No:" + destinationAccount.AccountNo;
            depositFrom.DepositAmount = depositRequest.Amount;
            _transactionRepository.Add(depositFrom);
            _transactionRepository.Add(depositTo);
            _uow.Commit();

            var transactionResponse = new TransactionResponse
                                          {
                                              Amount = depositRequest.Amount,
                                              DestinationAccountNumber =
                                                  destinationAccount.AccountNo + "-" +
                                                  destinationAccount.AccountTypeDiscription,
                                              SourceAccountNumber =
                                                  sourceAccout.AccountNo + "-" + sourceAccout.AccountTypeDiscription,
                                              TransactionType = "Deposit",
                                              RefNo = depositFrom.Id

                                          };

            return transactionResponse;

        }
    }
}
